Website Change Monitor & Diff Tracker avatar

Website Change Monitor & Diff Tracker

Pricing

from $100.00 / 1,000 site monitoreds

Go to Apify Store
Website Change Monitor & Diff Tracker

Website Change Monitor & Diff Tracker

Monitor any website for content changes with automatic diff detection. Track pricing pages, competitor sites, ToS updates, and more. Compares snapshots, reports added/removed text, and supports CSS selector targeting for precise monitoring.

Pricing

from $100.00 / 1,000 site monitoreds

Rating

0.0

(0)

Developer

Ryan Clinton

Ryan Clinton

Maintained by Community

Actor stats

0

Bookmarked

18

Total users

4

Monthly active users

8 days ago

Last modified

Share

Website Change Monitor

Website Change Monitor — website risk & competitive intelligence

Website risk & competitive intelligence — for competitors, compliance, vendors, SEO, and uptime.

The output is not a diff. It's a decision.

Most website monitors tell you that a page changed. Website Change Monitor tells you what changed, why it matters, how significant it is, and whether you should act -- as a structured decision your automation branches on, not a raw diff you have to read.

It is built to catch the changes that carry business risk: competitor pricing moves, compliance drift, SEO regressions, vendor policy changes, and outages -- and to tell you which ones deserve attention.

{
"changeCategory": "pricing",
"changeSeverity": "critical",
"recommendedAction": "review-now",
"likelyNoise": false
}

It monitors five layers of every page (visible content, SEO metadata, links, structured data, technology stack), classifies and scores each change, suppresses date/counter noise so you stop getting spammed by meaningless diffs, and tracks each page's volatility over time. Schedule it hourly, daily, or weekly from the Apify console -- no code required.


Most monitors vs Website Change Monitor

CapabilityTypical change monitorWebsite Change Monitor
Detect a page changedyesyes
Classify what changednoyes
Score severity + risknoyes
Detect pricing changesnoyes
Compliance drift (vs approved version)noyes
SEO / metadata / structured-data monitoringnoyes
Vendor risk monitoringnoyes
Competitive intelligence signalsnoyes
Suppress date/counter noiselimitedyes
Track volatility + trendsnoyes
Recommend an actionnoyes

Most website monitors return this:

{ "changed": true }

...which leaves every real question unanswered: Is it important? Is it pricing or just a footer tweak? Is it a legal change? Should you wake someone up? Or is it just a timestamp?

Website Change Monitor returns this:

{
"changeCategory": "pricing",
"changeSeverity": "critical",
"recommendedAction": "review-now",
"alertPriority": "critical",
"likelyNoise": false,
"whyItMatters": "High-impact change: pricing or plan details changed. Review now."
}

Your workflow already knows what to do. Every field is a stable enum you can branch on in Zapier, Make, n8n, Slack rules, or an AI agent tool call -- no prose parsing.

A traditional monitor stops at { "changed": true } -- then a human has to open the page, read the diff, decide whether it matters, decide who owns it, and decide whether to act. Website Change Monitor has already done that work before the alert reaches you.


Built for expensive mistakes

This actor is designed for the website changes that cost money when missed:

  • a competitor raises prices and your pricing is now stale
  • a vendor quietly changes SLA, support, or security terms
  • a privacy policy or terms page drifts from the version legal approved
  • an SEO regression (removed canonical, dropped Product schema) tanks rankings
  • an API doc ships a breaking change before your integration is ready
  • a page goes down or redirects somewhere unexpected

It is not built to wake you up for a footer tweak, a "Last updated" date, or a view counter -- those are classified as noise and suppressed.

Unique capability: compliance drift detection

Every other monitor compares yesterday vs today. Website Change Monitor can compare the approved version vs the live version: set baselineMode: "locked" and it reports whether a page has drifted from what legal/compliance signed off on, with daysOutOfCompliance and a deviationCount -- a genuinely different question that ordinary diff tools cannot answer.


What happens when you miss these changes?

A diff that says { "changed": true } only matters if a human notices it in time. Here is what each missed change costs -- and what the actor hands you instead.

ChangeCost of missing itWhat the actor returns
Competitor raises pricesYou leave money on the table for weekscompetitorSignals: ["pricing-increase"], riskScore: 91, recommendedAction: "review-now"
Vendor weakens its SLAYou find out during an incident, when it's too latevendorRiskSignals: ["sla-change"], recommendedAction: "review-soon"
Canonical / Product schema removedRanking loss and an organic-traffic decline you can't explainchangeCategory: "seo", primaryChangeLayer: "metadata"
Privacy policy drifts from approvedCompliance exposure on a page legal already signed offcomplianceStatus: "drifted", daysOutOfCompliance: 12
A monitored page goes downSilent outage until a customer reports itincidentType: "site-outage", recommendedAction: "review-now"

Why a diff isn't enough

A traditional monitor hands you { "changed": true } -- and a human still has to read the diff, assess the impact, find the owner, decide the urgency, and create the action. Website Change Monitor does that decision support work before the alert reaches you: the category, the risk score, the severity, the persona, and the recommended action are already determined. You consume a decision, not a diff.


What jobs does this solve?

Pick a profile and the actor configures the right layers and keywords for the job.

Detect competitor moves

Set profile: "competitor" and point it at rival pricing and feature pages. The actor watches pricing, plans, features, and tech-stack changes and emits deterministic competitorSignals (new-pricing-tier, pricing-increase, feature-added, free-trial-removed, product-discontinued) so your team sees the move, not just the diff.

{ "urls": ["https://rival.example.com/pricing", "https://rival.example.com/features"], "profile": "competitor", "kvStoreName": "competitor-intel" }

Set profile: "legal" with baselineMode: "locked" to freeze an approved version and alert on any deviation from it. Changes are categorised legal, given a compliance block (complianceStatus / daysOutOfCompliance / deviationCount), and routed to your reviewer.

{ "urls": ["https://partner.example.com/terms"], "profile": "legal", "baselineMode": "locked", "kvStoreName": "compliance" }

Track SEO changes

Set profile: "seo" to monitor titles, meta descriptions, canonicals, hreflang, structured data, and links. A removed JSON-LD Product block or a changed canonical is caught even when the visible text is identical -- reported under layerChanges with category seo or structured-data.

{ "urls": ["https://example.com/landing"], "profile": "seo", "kvStoreName": "seo-watch" }

Monitor vendor risk

Set profile: "vendor" and point it at a supplier's terms, privacy, security, status, and pricing pages. The actor emits deterministic vendorRiskSignals (terms-changed, privacy-policy-updated, security-page-changed, sla-change, support-policy-change, status-incident, pricing-increase) so vendor-management and procurement catch policy shifts without reading every page.

{ "urls": ["https://vendor.example.com/terms", "https://vendor.example.com/security", "https://status.vendor.example.com"], "profile": "vendor", "kvStoreName": "vendor-risk" }

Detect competitor pricing changes

Set profile: "pricing" and point it at competitor pricing pages. The actor watches prices, plans, and discount terms, classifies the change as pricing, and escalates it to critical when a price moves, with a paste-ready whyItMatters line.

{ "urls": ["https://competitor.example.com/pricing"], "profile": "pricing", "kvStoreName": "competitor-pricing" }

Watch API documentation

Set profile: "docs" plus watchKeywords: ["deprecated", "breaking change"] to watch API reference and changelog pages for deprecations, breaking changes, and endpoint edits that break your integration.

{ "urls": ["https://docs.example.com/api"], "profile": "docs", "watchKeywords": ["deprecated", "breaking change"], "kvStoreName": "api-docs" }

Why use Website Change Monitor?

Decisions, compliance drift, competitor & vendor signals, noise suppression

Naive change monitors create alert fatigue. They fire an alert every time a "Last updated" date or a view counter ticks over, training you to ignore them -- so you miss the change that actually mattered. Meanwhile the real risks pile up: competitors quietly change pricing, regulators revise policies, partners alter terms, and API docs ship breaking changes without notice.

Website Change Monitor solves both ends. It fetches each URL, detects what changed across five page layers, then adds a deterministic intelligence layer: it categorises the change, scores its magnitude 0-100, flags whether it is likely just noise, and tells you whether to review it now, review it soon, monitor it, or ignore it. Combined with Apify's scheduling and integrations, you get an alerting system that watches your most important pages around the clock and only escalates what actually matters.

Put simply, Website Change Monitor is a website risk intelligence actor: it does not just monitor websites, it monitors business risk on websites -- competitor, compliance, vendor, SEO, and operational -- and turns each change into a decision.


Key features

  • Multi-layer monitoring -- watches five layers of each page (visible content, SEO metadata, links, structured-data, technology stack) and catches changes the text diff misses, like a removed Product schema or a dropped analytics tag.
  • Classification + risk score -- every change gets a changeCategory, a changeSeverity tier, a sortable riskScore (0-100), and a businessImpact/changePersona -- so you know what kind of change it is, not just how big.
  • Noise suppression -- date/counter-only edits are flagged likelyNoise and suppressed with ignoreNoise. The cure for alert fatigue.
  • A recommended action, not a diff -- every change carries recommendedAction (review-now / review-soon / monitor / ignore) + alertPriority -- stable enums your automation branches on.
  • Job profiles -- one profile (pricing, legal, seo, competitor, vendor, docs, ecommerce, jobs) auto-configures layers + keywords for the job.
  • Business signals -- deterministic competitorSignals (pricing-increase, feature-added, ...) and vendorRiskSignals (sla-change, terms-changed, ...) -- competitive and vendor intelligence without an LLM.
  • Compliance drift -- baselineMode: "locked" reports complianceStatus / daysOutOfCompliance against an approved baseline.
  • Cross-run trend intelligence -- with a named KV store each URL accumulates volatility, changeVelocity, riskTrend, healthTrend, escalationHistory, and an expectedNextChange estimate.

Advanced intelligence features

  • Incident detection -- incidentType classifies outages, access errors, removed pages, and unexpected redirects from page health.
  • Self-tuning monitoring advice -- per-URL monitoringRecommendation (observed vs recommended frequency, mode, reason) derived from the page's own history.
  • Monitoring confidence -- monitoringConfidence (level + reasons) tells you how reliably a page is tracked.
  • Page fingerprints -- per-layer SHA-256 hashes for audit trails and deployment verification.
  • Primary change layer -- a one-glance primaryChangeLayer triage field.
  • Executive rollups -- run summary actionQueue + portfolioSummary (criticals, compliance risks, pricing/SEO changes, incidents, per-persona breakdown, one actionRequired flag).
  • Watch keywords + severity gating -- escalate on watchKeywords; minSeverity outputs only changes at/above a threshold.

Reliability & output

  • Three comparison modes (text / HTML / CSS selector), ignore-rules engine (ignorePatterns regex + ignoreSelectors CSS), selector auto-discovery, and site-wide discovery via discoverFromSitemap.
  • Page-health tracking (status, response time, redirect, size delta) on every check.
  • Robust fetching -- body-bounded timeout + retry on transient failures; errors become typed records, never a crash.
  • Output profiles (minimal / standard / full), per-URL change history in a named KV store, multi-URL batch with per-URL isolation, and enriched webhook + Slack payloads.

How to use Website Change Monitor

Using Apify Console

  1. Navigate to the actor page -- go to Website Change Monitor on Apify and click "Try for free" or "Start."
  2. Enter your URLs -- add one or more website URLs in the "URLs to Monitor" field. You can paste them one per line or use the string list editor.
  3. Select a comparison mode -- choose "Text" for clean visible-text comparison (recommended), "HTML" for full markup comparison, or "CSS Selector" to target a specific page section like .pricing or #main-content.
  4. Set a named KV Store -- enter a name in the "KV Store Name" field (e.g., my-website-snapshots) so snapshots persist between scheduled runs. Without this, snapshots are lost after each run and every execution will report all URLs as "new."
  5. Run or schedule -- click "Start" for a one-off run, or configure a schedule (hourly, daily, weekly) from the Apify console to enable continuous monitoring.

Using the API

You can trigger the actor programmatically via the Apify API. Send a POST request to the actor's run endpoint with your input JSON in the request body. See the API and Integration section below for code examples in Python, JavaScript, and cURL.


Input parameters

FieldTypeRequiredDefaultDescription
urlsstring[]Yes--List of website URLs to monitor for content changes.
modestringNo"text"Comparison mode: text (visible text only), html (full HTML markup), or selector (CSS selector targeting).
cssSelectorstringNo--CSS selector to target a specific page section (e.g., .pricing, #main-content). Only used when mode is selector.
notifyOnlyChangesbooleanNotrueIf true, only output URLs with status changed, new, or error. If false, output all monitored URLs.
kvStoreNamestringNo--Named Key-Value store for persistent snapshot + history storage across runs. If empty, uses the default actor KV store.
watchKeywordsstring[]No--Keywords to watch for in the diff. A change containing any of them is escalated to at least major severity and the matched terms appear in keywordHits.
minSeveritystringNo"cosmetic"Only output and notify on changes at or above this severity: cosmetic, minor, major, or critical. Errors and first-run baselines always pass.
ignoreNoisebooleanNofalseIf true, changes where only dates, timestamps, or numeric counters changed are suppressed from output and notifications.
outputProfilestringNo"standard"Field detail per record: minimal (decision fields only), standard, or full (includes per-URL change history and full layer diffs).
layersstring[]No["content"]Layers to monitor: content, metadata, links, structured-data, technology. Empty uses the profile's default.
profilestringNo"general"Use-case preset that auto-configures layers + keywords: pricing, legal, ecommerce, seo, docs, jobs, competitor, vendor, custom. Explicit layers/watchKeywords override it. For compliance, pair profile: "legal" with baselineMode: "locked".
baselineModestringNo"latest"latest compares to the previous run; locked compares to a frozen approved baseline (compliance drift detection).
ignorePatternsstring[]No--Regex patterns; matching lines are stripped before hashing/diffing. Kills false positives like "Last updated 2026-01-01".
ignoreSelectorsstring[]No--CSS selectors removed from the page before extraction (e.g. .timestamp, .view-counter).
autoDetectSectionsbooleanNofalseReturn suggestedSelectors (monitorable CSS sections) per page.
historyDepthintegerNo20Recent changes retained per URL for trend analysis (max 200). Requires a named KV store.
discoverFromSitemapbooleanNofalseDiscover pages from each seed URL's sitemap.xml and add them to the monitored set.
maxDiscoveredPagesintegerNo50Cap on sitemap-discovered pages per seed (max 500).
webhookUrlstringNo--HTTP endpoint to POST change notifications to. Payload carries url, changeType, severity, category, magnitude, recommendedAction, alertPriority, keywordHits, whyItMatters, and snapshots.
slackWebhookUrlstringNo--Slack incoming webhook URL. Sends a formatted message with the change priority, category, plain-English summary, and a diff preview.

Example input

{
"urls": [
"https://competitor.example.com/pricing",
"https://partner.example.com/terms",
"https://docs.example.com/api/changelog"
],
"mode": "text",
"notifyOnlyChanges": true,
"watchKeywords": ["price", "discontinued", "deprecated"],
"minSeverity": "minor",
"ignoreNoise": true,
"kvStoreName": "my-website-snapshots"
}

Tips for input

  • Always set a named KV Store for scheduled runs. Without it, the default KV store is ephemeral and every run reports all URLs as "new."
  • Use CSS selector mode to reduce noise from ads, timestamps, or dynamic content. Target .main-content, #article-body, or similar stable selectors.
  • Start with text mode unless you need to detect HTML structural changes. Text mode strips scripts, styles, SVG, and non-visible elements for cleaner comparisons.
  • Split very large URL lists (500+) across multiple scheduled runs to stay within timeout limits.

Output

Sample output: url, category, severity, risk score, recommended action, business impact

Each per-URL item carries recordType: "change-event". Here is a detected pricing change with the full intelligence + monitoring layers:

{
"recordType": "change-event",
"schemaVersion": "2.0.0",
"url": "https://competitor.example.com/pricing",
"status": "changed",
"previousSnapshot": "Basic Plan $9/mo\nPro Plan $29/mo\nEnterprise Plan $99/mo",
"currentSnapshot": "Basic Plan $12/mo\nPro Plan $35/mo\nEnterprise Plan $99/mo\nNew: Startup Plan $19/mo",
"changes": {
"addedText": ["Basic Plan $12/mo", "Pro Plan $35/mo", "New: Startup Plan $19/mo"],
"removedText": ["Basic Plan $9/mo", "Pro Plan $29/mo"],
"summary": "3 line(s) added, 2 line(s) removed",
"addedCount": 3,
"removedCount": 2,
"percentChanged": 71
},
"intelligence": {
"changeMagnitude": 78,
"changeSeverity": "critical",
"changeCategory": "pricing",
"riskFlags": ["PRICE_TERMS", "NUMBERS_CHANGED", "KEYWORD_MATCH"],
"keywordHits": ["price"],
"likelyNoise": false,
"noiseReasons": [],
"recommendedAction": "review-now",
"alertPriority": "critical",
"whyItMatters": "High-impact change: pricing or plan details changed (3 line(s) added, 2 removed). Review now. Matched watched keyword(s): price.",
"significanceDrivers": ["5 line(s) changed", "category: pricing", "keyword hit: price"]
},
"monitoring": {
"firstSeenAt": "2026-01-02T10:00:00.000Z",
"runsSeen": 46,
"timesChanged": 4,
"lastChangedAt": "2026-01-29T10:00:00.000Z",
"daysSinceLastChange": 19,
"changeFrequency": "occasional",
"recentHistory": [
{ "at": "2026-01-29T10:00:00.000Z", "magnitude": 22, "severity": "minor", "category": "content" }
]
},
"lastChecked": "2026-02-17T10:00:00.000Z",
"previousChecked": "2026-02-16T10:00:00.000Z",
"contentHash": "a3f2b8c1d9e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0"
}

A change suppressed as noise (only a timestamp moved) -- reported with likelyNoise: true unless you set ignoreNoise:

{
"recordType": "change-event",
"url": "https://news.example.com",
"status": "changed",
"intelligence": {
"changeMagnitude": 4,
"changeSeverity": "cosmetic",
"changeCategory": "date-or-counter",
"likelyNoise": true,
"noiseReasons": ["Only dates, times, or numeric counters changed."],
"recommendedAction": "ignore",
"alertPriority": "low"
}
}

Every run ends with one summary record (also written to the SUMMARY KV key):

{
"recordType": "summary",
"schemaVersion": "2.0.0",
"totalChecked": 20,
"changed": 3,
"new": 0,
"unchanged": 16,
"errors": 1,
"noiseSuppressed": 4,
"highestPriority": "critical",
"bySeverity": { "critical": 1, "major": 1, "minor": 1, "cosmetic": 0, "none": 0 },
"byCategory": { "pricing": 1, "legal": 1, "content": 1 },
"topChanges": [
{ "url": "https://competitor.example.com/pricing", "severity": "critical", "category": "pricing", "magnitude": 78 }
],
"actionRequired": true
}

Output fields

FieldTypeDescription
recordTypestringchange-event, summary, or error.
schemaVersionstringOutput contract version (semver), additive within a major version.
urlstringThe monitored URL.
statusstringOne of changed, unchanged, new, or error.
intelligenceobjectDecision layer on changed pages: riskScore (0-100, sortable), changeMagnitude (0-100), changeSeverity, changeCategory, businessImpact, changePersona, competitorSignals[], vendorRiskSignals[], riskFlags[], keywordHits[], likelyNoise, recommendedAction, alertPriority, whyItMatters.
primaryChangeLayerstringWhich layer changed most: content, metadata, links, structured-data, technology.
monitoringobjectCross-run lifecycle + trend: firstSeenAt (first observed), runsSeen, timesChanged, lastChangedAt (last meaningful change), daysSinceLastChange, changeFrequency, volatilityScore, volatilityBand, changeVelocity, stabilityIndex, averageDaysBetweenChanges, expectedNextChange, healthTrend, escalationHistory, recentHistory[]. (lastChecked at the record root is the last-observed time.)
layerChangesobjectPer-layer diff when extra layers are monitored: metadata (SEO field changes), links (internal/external added/removed), structuredData (schema types added/removed), technology (tags added/removed).
healthobjectstatusCode, responseTimeMs, redirected, finalUrl, contentSizeBytes, sizeChangePercent.
incidentTypestringOperational incident class: site-outage, access-error, page-removed, redirect-change, slow-response. Absent when healthy.
monitoringRecommendationobjectSelf-tuning advice: recommendedMode, recommendedFrequency, reason.
complianceobjectLocked-baseline drift: complianceStatus, daysOutOfCompliance, deviationCount.
monitoringConfidenceobjectHow reliably the page is tracked: level (high/medium/low) + reasons[].
pageFingerprintobjectPer-layer SHA-256 hashes for audit / deployment verification.
suggestedSelectorsstring[]Monitorable CSS selectors detected on the page (only when autoDetectSections is on).
changesobject or nulladdedText[], removedText[], summary, addedCount, removedCount, percentChanged. Null on error.
previousSnapshotstring or nullTruncated content from the previous run (null on first run / error / minimal profile).
currentSnapshotstringTruncated content from the current run (empty on error).
lastCheckedstringISO 8601 timestamp of the current check.
previousCheckedstring or nullISO 8601 timestamp of the previous check (null if first run).
contentHashstringSHA-256 hash of the current content (empty on error).
errorMessagestringPresent only when status is error. Describes the failure reason.
failureTypestringOn error records: timeout, blocked, not-found, server-error, network, fetch-error, invalid-input, or actor-error. Lets you tell "site is blocking us" from "page is gone".
jsWarningstringPresent when the page looks like a JavaScript app (React / Next.js / Nuxt / Angular / Vue) that renders content client-side, so raw-HTML monitoring may be inaccurate.

Decision enums (stable, branch on these)

FieldValues
intelligence.changeSeveritycritical, major, minor, cosmetic, none
intelligence.changeCategorypricing, availability, legal, contact, navigation, content, date-or-counter, seo, links, structured-data, technology, unknown
intelligence.recommendedActionreview-now, review-soon, monitor, ignore
intelligence.alertPrioritycritical, high, medium, low, none
intelligence.businessImpacthigh, medium, low
intelligence.changePersonabusiness-critical, compliance-risk, seo-risk, operational, cosmetic, noise
incidentTypesite-outage, access-error, page-removed, redirect-change, slow-response
monitoring.changeFrequencyvolatile, active, occasional, stable, new
monitoring.volatilityBandhigh, medium, low, stable
monitoring.changeVelocityaccelerating, slowing, steady, unknown
monitoring.riskTrendworsening, improving, stable, unknown
monitoring.healthTrenddegrading, improving, stable, unknown

Use cases

In practice: point it at 25 competitor pricing and feature pages on a daily schedule with profile: "competitor" and minSeverity: "major", wire the webhook to Slack, and the only thing that reaches your channel is the handful of review-now pricing and feature moves -- everything else is classified, scored, and filed without pinging anyone.

  • Competitor pricing intelligence -- monitor competitor pricing pages to detect price changes, new tiers, or removed plans before they affect your market positioning.
  • Compliance and legal monitoring -- track terms-of-service, privacy policy, and regulatory notice pages for changes that may require legal review or policy updates.
  • SEO and content tracking -- watch competitor landing pages, meta content, and blog posts for changes that could impact your search strategy.
  • API documentation monitoring -- detect changelog updates, deprecation notices, or breaking changes in third-party API docs before they cause production issues.
  • Brand protection -- monitor pages that reference your brand to detect unauthorized modifications, counterfeit listings, or partner compliance violations.
  • E-commerce inventory and availability -- track product pages for stock status changes, new product launches, or description updates.
  • Government and regulatory watch -- monitor federal registers, agency announcements, or grant program pages for new postings and policy revisions.
  • Deployment verification -- confirm your own website deployments went live correctly by comparing pre-deployment and post-deployment snapshots.
  • News and media monitoring -- track press release pages, newsrooms, or publication landing pages for new content as it appears.
  • Academic and research tracking -- watch institutional pages, journal tables of contents, or conference sites for new publications and calls for papers.

Deliver alerts to Slack and Notion (MCP connectors)

Send each run's change alerts straight into the apps your team uses, via Apify MCP connectors — a token-free alternative to the Slack webhook. Set notionConnector or slackConnector and the run delivers the severity counts plus the top changes. Your credentials stay encrypted on your Apify account; the actor never sees your Slack or Notion token.

  • Slack — posts a digest (sites checked, changed, critical/major counts, action-required flag) and the top changes to a channel. One-click OAuth in Apify Console → Settings → MCP connectors.
  • Notion — archives each run. notionArchiveProfile: per-change writes one page per change; the default summary writes one page per run.
  • deliverTopN bounds how many changes are sent (default 10); the full record set always stays in the dataset.

Schedule the monitor and every run posts only what changed to your channel or Notion. Delivery is additive — unset connectors and the actor behaves exactly as before; each run records a deliveries block on its SUMMARY.

API & Integration

Python

from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run_input = {
"urls": [
"https://competitor.example.com/pricing",
"https://partner.example.com/terms"
],
"mode": "text",
"notifyOnlyChanges": True,
"kvStoreName": "my-website-snapshots"
}
run = client.actor("qcxKU2ReRjP5NmlZR").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(f"{item['url']} -- {item['status']}")
if item["status"] == "changed":
print(f" Summary: {item['changes']['summary']}")

JavaScript

import { ApifyClient } from "apify-client";
const client = new ApifyClient({ token: "YOUR_API_TOKEN" });
const run = await client.actor("qcxKU2ReRjP5NmlZR").call({
urls: [
"https://competitor.example.com/pricing",
"https://partner.example.com/terms"
],
mode: "text",
notifyOnlyChanges: true,
kvStoreName: "my-website-snapshots"
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
console.log(`${item.url} -- ${item.status}`);
if (item.status === "changed") {
console.log(` Summary: ${item.changes.summary}`);
}
});

cURL

curl -X POST "https://api.apify.com/v2/acts/qcxKU2ReRjP5NmlZR/runs?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://competitor.example.com/pricing"],
"mode": "text",
"notifyOnlyChanges": true,
"kvStoreName": "my-website-snapshots"
}'

Integrations

Website Change Monitor works with the Apify platform's built-in integrations:

  • Webhooks -- trigger an HTTP POST to your endpoint whenever the actor finishes, sending change data to your backend.
  • Zapier -- connect the output to 5,000+ apps for email, Slack, SMS, or spreadsheet alerts.
  • Make (Integromat) -- build multi-step automation workflows that filter by status and route alerts.
  • Google Sheets -- export change reports automatically for historical tracking and analysis.
  • Slack / Microsoft Teams -- receive instant notifications in your team channel when pages change.
  • Email notifications -- use Apify's built-in email integration for inbox alerts.
  • Apify API -- programmatically trigger runs, retrieve datasets, and manage schedules.

Use in Dify

Drop this actor into Dify workflows via the Apify plugin's Run Actor node. Each changed page returns classified, scored, and recommended as structured JSON -- review-now / review-soon / monitor / ignore plus the changeCategory (pricing / legal / availability / ...) and alertPriority your downstream node branches on. A plain change monitor pointed at the same page returns a raw line diff; this returns a decision.

  • Actor ID: ryanclinton/website-change-monitor
  • Sample input (watch competitor pricing + partner terms, suppress noise, only surface real changes):
{
"urls": [
"https://competitor.example.com/pricing",
"https://partner.example.com/terms"
],
"mode": "text",
"watchKeywords": ["price", "discontinued", "deprecated"],
"minSeverity": "minor",
"ignoreNoise": true,
"kvStoreName": "my-watchlist"
}

Branching on the decision

Dify's if/else node routes on equality matches, so the stable enums plug in directly. First filter to the records that matter with the recordType discriminator (change-event vs the run summary), then branch:

  • intelligence.recommendedAction == "review-now" → send a Slack alert / open a ticket
  • intelligence.recommendedAction == "review-soon" → add to a daily digest
  • intelligence.changeCategory == "pricing" → route to the competitive-intel channel
  • intelligence.changeCategory == "legal" → route to the compliance reviewer
  • intelligence.alertPriority == "critical" → page on-call
  • everything else → no-op

Because every category and severity is a stable enum (additive within a major version), the branches never break when new values are added. The intelligence.whyItMatters and intelligence.keywordHits[] fields are paste-ready -- drop them straight into the alert body, no LLM rewriting needed.

Opt-in modes Dify workflows can leverage

  • watchKeywords -- escalate any change containing a term you care about, and read which terms hit from keywordHits[].
  • minSeverity -- gate the whole run so only major/critical changes reach your workflow.
  • ignoreNoise -- drop date/counter-only edits before they ever reach a branch.
  • outputProfile: "minimal" -- strip snapshots and history so the if/else node sees only the decision fields.
  • kvStoreName -- persist snapshots + history across scheduled runs so monitoring.changeFrequency tells you which pages are volatile.

How it works

Intelligence stack: from fetched page through diff, classification, scoring, signals to decisions

Website Change Monitor follows a sequential pipeline for each URL in the input list:

  1. Fetch -- the actor sends an HTTP GET request to each URL with a 30-second timeout using AbortController and a custom ApifyBot/1.0 User-Agent header.
  2. Extract -- based on the selected mode, content is extracted: text mode uses Cheerio to strip <script>, <style>, <noscript>, <svg>, and <head> elements then normalizes whitespace; html mode uses the raw response; selector mode uses Cheerio to extract text from elements matching the CSS selector.
  3. Hash -- the extracted content is passed through SHA-256 (crypto.createHash) to produce a 64-character hex digest.
  4. Compare -- the hash is compared against the previously stored hash in the Key-Value store. If no previous snapshot exists, the URL is marked as new.
  5. Diff -- when hashes differ, a line-by-line Set-based comparison identifies lines present in the old snapshot but missing from the new (removed) and lines present in the new but missing from the old (added).
  6. Store -- the current content, hash, and timestamp are saved to the Key-Value store under the key snapshot_{sha256(url)} for comparison on the next run.
  7. Output -- results are filtered based on the notifyOnlyChanges setting and pushed to the dataset.
URL List
|
v
[Fetch HTML] --error--> { status: "error" }
|
v
[Extract Content]
| text: strip tags, normalize whitespace
| html: raw markup
| selector: CSS target extraction
|
v
[SHA-256 Hash]
|
v
[Load Previous Snapshot from KV Store]
|
+--> No previous? --> { status: "new" } --> [Store Snapshot]
|
+--> Hash matches? --> { status: "unchanged" }
|
+--> Hash differs? --> [Line-by-Line Diff] --> { status: "changed" } --> [Store Snapshot]
|
v
[Filter by notifyOnlyChanges]
|
v
[Push to Dataset]

Performance & cost

Website Change Monitor runs on the Apify platform. You receive $5 of free platform credits monthly, which covers substantial monitoring workloads.

ScenarioURLsFrequencyEst. Monthly Cost
Light monitoring5 URLsDaily~$0.50
Standard monitoring20 URLsDaily~$1.50
Frequent checks20 URLsEvery 6 hours~$5.00
Heavy monitoring100 URLsDaily~$7.00
Enterprise100 URLsHourly~$40.00

The actor uses minimal memory (256 MB) and completes quickly since it only fetches and compares text content. Actual costs depend on page size and number of URLs. One actor compute unit (1 CU) costs approximately $0.25 at 256 MB memory.


Limitations

  • No JavaScript rendering -- the actor fetches raw HTML via HTTP GET. Pages that rely on client-side JavaScript to render content will not be monitored accurately. The actor detects this case and sets a jsWarning field naming the framework (React / Next.js / Nuxt / Angular / Vue) so you know to switch to a browser-based scraper, but it does not render the page itself.
  • No authentication support -- the actor cannot log in or pass session tokens. Only publicly accessible pages can be monitored.
  • Sequential processing -- URLs are processed one at a time with a 30-second timeout each. Very large batches (500+ URLs) may require splitting across multiple runs.
  • Line-level granularity only -- the diff algorithm operates on whole lines using Set comparison. It does not detect character-level or word-level changes within a line.
  • Dynamic content noise -- pages with timestamps, session IDs, ad rotations, or A/B test variants may trigger false positives. Use ignorePatterns, ignoreSelectors, CSS selector mode, or ignoreNoise to suppress them.
  • No visual / screenshot monitoring -- this actor compares the page's HTML and its extracted layers (content, SEO, links, structured data, technology). It does not render the page or compare screenshots for layout/pixel changes. Visual layout monitoring needs a full browser engine and is a separate, heavier tool.
  • Snapshot truncation in output -- dataset output truncates snapshots to 1,000 characters for readability. Full content is stored in the KV store but is not directly visible in dataset items.
  • Single User-Agent -- all requests use the ApifyBot/1.0 User-Agent. Some websites may block or rate-limit this agent string.

Responsible use

  • Respect robots.txt -- check that the websites you monitor allow automated access. Some sites explicitly block bots in their robots.txt or terms of service.
  • Set reasonable frequencies -- avoid scheduling checks every few minutes unless necessary. Hourly or daily checks are sufficient for most monitoring use cases and reduce load on target servers.
  • Monitor only public content -- do not attempt to use this actor to access paywalled, authenticated, or restricted content. It is designed for publicly available web pages.
  • Comply with local laws -- ensure your monitoring activities comply with applicable data protection regulations, computer access laws, and website terms of service in your jurisdiction.
  • Avoid excessive load -- when monitoring large URL lists, consider staggering runs or splitting lists to prevent sending too many requests to a single domain in a short period.

FAQ

How does the actor detect changes? It fetches each URL, extracts content based on your chosen mode (text, HTML, or CSS selector), generates a SHA-256 hash of the extracted content, and compares it against the hash stored from the previous run. If the hashes differ, it performs a line-by-line diff to identify exactly what was added and removed.

What happens on the first run? On the first run for any URL, the actor captures a baseline snapshot and stores it in the Key-Value store. The URL is reported with status new and the summary reads "First snapshot captured. Changes will be reported on next run."

Do I need a named KV Store? For one-off runs, the default store works fine. For scheduled or recurring runs, you should always set a named KV Store so snapshots persist between runs. Without it, every run treats every URL as new.

Can I monitor pages behind a login? No. The actor makes unauthenticated HTTP GET requests only. It works with publicly accessible web pages.

What if a page returns an error? The actor reports it with status error and includes the error message (e.g., "HTTP 503 Service Unavailable" or "Fetch failed: The operation was aborted"). Other URLs in the same batch continue processing normally.

How do I reduce false positives from dynamic content? Use CSS selector mode to target only the stable section of the page you care about, such as .pricing-table, #terms-content, or article.main. This avoids false alerts from ads, timestamps, session tokens, or other elements that change on every page load.

Can I monitor hundreds of URLs in a single run? Yes. The actor processes URLs sequentially with a 30-second timeout per URL. For very large batches (500+ URLs), consider splitting them across multiple scheduled runs to stay within execution time limits.

What is the difference between text, HTML, and selector modes? Text mode uses Cheerio to strip all non-visible elements (scripts, styles, SVG, head) and normalizes whitespace for the cleanest comparison. HTML mode compares the full raw markup, catching structural changes but producing noisier diffs. Selector mode extracts text only from elements matching your CSS selector, giving you surgical precision over what gets compared.

How are snapshots stored? Each URL's content, hash, and timestamp are stored in the Key-Value store under the key snapshot_{sha256(url)}. The URL is hashed to produce a safe key without special characters.

Does the actor support webhook notifications? Yes, in two ways. First, you can set the webhookUrl input parameter to receive a JSON POST directly from the actor the moment a change is detected — no need to wait for the run to finish. Second, you can configure Apify platform webhooks to trigger when the entire run completes. For Slack, use the slackWebhookUrl input to get formatted change alerts with diff previews sent directly to your channel.

What does the notifyOnlyChanges flag do? When set to true (the default), the actor only pushes items with status changed, new, or error to the dataset. Unchanged URLs are silently skipped. Set it to false to include all URLs in the output regardless of status.

Can I use this to verify my own website deployments? Yes. Add your own pages to the URL list and run the actor after a deployment. If the content hash changes as expected, your deployment went live. If it does not change, something may have gone wrong.


Help us improve

If you encounter issues, you can help us debug faster by enabling run sharing in your Apify account:

  1. Go to Account Settings > Privacy
  2. Enable Share runs with public Actor creators

This lets us see your run details when something goes wrong, so we can fix issues faster. Your data is only visible to the actor developer, not publicly.


ActorDescriptionLink
Website Contact ScraperExtract emails, phone numbers, and social links from any website.Open
Website Content to MarkdownConvert web pages to clean Markdown format for documentation and archiving.Open
Website Tech Stack DetectorIdentify the technologies, frameworks, and tools powering any website.Open
WHOIS Domain LookupLook up domain registration details including registrar, expiry dates, and nameservers.Open
Brand Protection MonitorMonitor the web for unauthorized use of your brand name and trademarks.Open
SERP Rank TrackerTrack your keyword rankings across search engines over time.Open